Popular Searches
Popular Course Categories
Popular Courses

Spring Boot 3.x + Virtual Threads: Building High-Performance REST APIs in 2026

What Our Students Say
Spring Boot 3 Virtual Threads Java 21 High Performance REST API Development Guide 2026

A Complete Developer Guide to Using Java 21 Virtual Threads with Spring Boot 3 for Scalable, High-Throughput REST API Development in 2026

If you are building REST APIs with Spring Boot in 2026, there is one feature you cannot afford to ignore: Virtual Threads. Introduced as a stable, production-ready feature in Java 21 (Project Loom) and natively supported in Spring Boot 3.2 and later, Virtual Threads fundamentally change how Java handles concurrency. They let you write simple, familiar blocking code that scales like non-blocking reactive code.

This guide gives you everything: what Virtual Threads are, how they work under the hood, how to enable and configure them in Spring Boot 3.x, real performance benchmarks, pitfalls to avoid, and production-ready configuration. Whether you are a Java developer preparing for a senior role or a team lead evaluating the right architecture for a new microservice, this is your definitive Spring Boot Virtual Threads 2026 reference.

Want to master all of this with live project training and 100% placement support? JustAcademy's Full Stack Java Developer Bootcamp covers Spring Boot 3.x, Virtual Threads, Java 21, and everything else you need to land a high-paying Java role in 2026. Bootcamp link: https://www.justacademy.co/job-bootcamp-detail/full-stack-java-developer-bootcamp-online-live-interactive-training-with-real-projects

Table of Contents

  1. What Are Virtual Threads? A Clear Explanation
  2. Why Virtual Threads Matter for REST APIs in 2026
  3. Platform Threads vs. Virtual Threads: Key Differences
  4. Enabling Virtual Threads in Spring Boot 3.x — Step by Step
  5. Building a High-Performance REST API with Virtual Threads
  6. Performance Benchmarks: Virtual Threads vs. Platform Threads vs. WebFlux
  7. Database Integration: JPA, JDBC, and Connection Pools
  8. Pitfalls and Anti-Patterns to Avoid
  9. Production Configuration Checklist
  10. Frequently Asked Questions

1. What Are Virtual Threads? A Clear Explanation

Virtual Threads are lightweight threads introduced as part of Project Loom and delivered as a stable, production-ready feature in Java 21 LTS. Unlike traditional Java threads — which map one-to-one to operating system threads — Virtual Threads are managed entirely by the JVM. The JVM multiplexes millions of virtual threads onto a much smaller pool of real OS threads called carrier threads.

The core insight is this: most REST API work is I/O-bound. A thread blocking on a database query, an HTTP call, or a file read is sitting idle, consuming memory, but doing no useful CPU work. Traditional platform threads waste OS resources while they wait. Virtual Threads solve this elegantly. When a Virtual Thread blocks on I/O, the JVM unmounts it from its carrier thread, freeing that carrier to run other virtual threads. When the I/O completes, the JVM automatically remounts the virtual thread and it continues executing from exactly where it paused.

This happens transparently, with no changes to your code. Your blocking database call, your blocking HTTP request, your blocking file read — all behave exactly the same way from your code's perspective. But underneath, the JVM is efficiently multiplexing potentially millions of concurrent operations onto a small number of OS threads.

Platform Thread characteristics: one thread equals one OS thread, approximately 1 MB stack per thread fixed allocation, OS scheduler controls context switching, a blocked thread is a wasted OS resource, practical limit of five to ten thousand threads, and thread pool tuning is required.

Virtual Thread characteristics: millions of threads run on just a few OS threads, stack starts at a few kilobytes and grows dynamically, JVM scheduler handles cooperative scheduling, a blocked thread is unmounted and the carrier is freed, practical limit is millions of concurrent threads, and no pool is needed since you create one per task.

2. Why Virtual Threads Matter for REST APIs in 2026

The dominant challenge for REST API scalability has always been thread pool exhaustion under high concurrency. When ten thousand concurrent requests hit a Spring Boot application backed by a two-hundred-thread Tomcat pool, nine thousand eight hundred requests queue up and wait. Users see latency spikes. The only solution becomes horizontal scaling — adding more pods, more compute, more cost.

The fundamental shift in 2026 is this: with Virtual Threads, the question is no longer how many threads should my pool have. The question becomes how many concurrent requests can my database, cache, or downstream service actually handle. Virtual Threads move the bottleneck from thread management to actual resource limits, where it belongs.

The business case in 2026 is compelling for three reasons.

First, Java 21 LTS is the new enterprise baseline. Most enterprise teams have now migrated to Java 21, released in September 2023. Java 17 LTS end-of-life pressure has driven broad Java 21 adoption, making Virtual Threads production-available without any risky migration.

Second, Spring Boot 3.2 made enablement a one-line configuration change. Setting spring.threads.virtual.enabled=true is all it takes. Spring Boot handles Tomcat reconfiguration, async executor setup, and scheduled task executor configuration automatically.

Third, microservice economics favor Virtual Threads significantly. Higher throughput per instance means fewer Kubernetes pods, less orchestration overhead, lower cloud bills, and simpler operational profiles. For high-traffic microservices processing millions of daily requests, the infrastructure cost savings are measurable.

3. Platform Threads vs. Virtual Threads: Key Differences

OS Mapping: Platform threads map one thread to one OS thread. Virtual threads map many threads onto few OS threads.

Memory per thread: Platform threads use approximately 1 MB of fixed stack. Virtual threads start at a few kilobytes and grow dynamically as needed.

Maximum concurrent threads: Platform threads are limited to thousands by OS constraints. Virtual threads support millions, managed by the JVM.

Blocking behavior: A blocked platform thread holds its OS thread captive. A blocked virtual thread is unmounted from its carrier, freeing the carrier for other work.

Context switching: Platform thread context switching is OS-level and relatively expensive. Virtual thread switching is JVM-level and very cheap.

Thread pools: Platform threads require carefully tuned thread pools for performance. Virtual threads do not need pools — you create one per task.

Code style: Platform threads work with both blocking and reactive code. Virtual threads work with blocking, imperative code — no reactive patterns required.

CPU-bound suitability: Both platform threads and virtual threads are equally suited for CPU-intensive work. Virtual threads provide no advantage for CPU-bound tasks.

I/O-bound suitability: Platform threads are limited by pool size for I/O workloads. Virtual threads are excellent for I/O-bound workloads and scale to millions of concurrent operations.

Important caveat: Virtual Threads are not magic for CPU-bound work. They excel at I/O-bound concurrency — REST API calls, database queries, file reads, external service calls. For CPU-intensive computation such as image processing, complex calculations, or ML inference, platform threads with a properly sized pool are still the right tool. Virtual Threads will not speed up CPU-bound code. They will occupy carrier threads and potentially starve other virtual threads from running.

4. Enabling Virtual Threads in Spring Boot 3.x — Step by Step

Prerequisites before you begin: Java 21 or later (verify with java -version), Spring Boot 3.2 or later (Spring Boot 3.3 or 3.4 is recommended in 2026), and the spring-boot-starter-web dependency (the servlet-based Tomcat stack, not WebFlux).

Step 1: pom.xml Configuration

Set the parent to Spring Boot 3.3.5 or later. Set the java.version property to 21. Include the spring-boot-starter-web dependency for the Tomcat web server, spring-boot-starter-data-jpa for database access, and spring-boot-starter-actuator for monitoring and observability.

Step 2: Enable Virtual Threads in application.properties

The single most important setting is: spring.threads.virtual.enabled=true

This one property instructs Spring Boot to configure Tomcat, the async task executor, and the scheduled task executor to all use Virtual Threads automatically.

Additional server configuration: set server.tomcat.max-connections to 20000 to handle higher connection capacity. Set server.tomcat.accept-count to 1000. The server.tomcat.threads.max setting can remain reasonable at 200 since each thread is now a lightweight virtual thread.

Step 3: Verify Virtual Threads Are Active

Create a simple REST endpoint that returns Thread.currentThread().isVirtual(). When Virtual Threads are properly enabled, this will return true for every incoming request. The thread name will show as something like tomcat-handler-0, and the thread group will show as none since virtual threads do not belong to thread groups.

5. Building a High-Performance REST API with Virtual Threads

With Virtual Threads enabled, your Spring MVC code looks completely unchanged — that is the entire point. You write simple, blocking, imperative code and receive near-reactive scalability automatically.

Here is what the service layer looks like for a product catalog API that calls a database and two external HTTP services:

The ProductService class is annotated with @Service and @Transactional(readOnly = true). The getProductDetail method retrieves a product from the database using a blocking JPA call, then calls an inventory service for stock data, and then calls a pricing service for dynamic pricing — all three are blocking calls. With Virtual Threads, each blocking call unmounts the virtual thread from its carrier, freeing the carrier for other requests. There are no callbacks, no CompletableFuture chains, no reactive operators. The code is exactly as readable as single-threaded code.

For bulk operations, Java 21 introduces Structured Concurrency through StructuredTaskScope. You can fan out multiple concurrent calls using scope.fork() for each individual item, then call scope.join().throwIfFailed() to wait for all of them and handle failures cleanly. This pattern replaces complex parallel stream or CompletableFuture.allOf() patterns with clean, readable, structured code.

The REST controller layer is equally straightforward — standard @RestController with @GetMapping and @PostMapping annotations. No @Async annotation needed. No reactive return types. The controller calls the service with a simple blocking method call. The Virtual Thread handling happens transparently below.

Key takeaway: the controller and service code is completely standard blocking Spring MVC. Virtual Threads give you the performance of reactive programming with the simplicity of imperative code. This is the core value proposition of Spring Boot Virtual Threads in 2026.

6. Performance Benchmarks: Virtual Threads vs. Platform Threads vs. WebFlux

These benchmarks represent a typical microservice scenario: a REST endpoint that performs a 50ms simulated database call and a 30ms simulated external HTTP call, measured at 5,000 concurrent users.

Throughput results: Virtual Threads with Spring Boot 3.3 achieve approximately 12,400 requests per second. Spring WebFlux achieves approximately 12,800 requests per second — effectively identical. Platform Threads with a 200-thread pool (typical Spring Boot 2.x default) achieve only 3,100 requests per second, severely bottlenecked by pool exhaustion. Platform Threads with a 1,000-thread pool achieve approximately 6,200 requests per second but at significant memory cost.

Memory usage at 5,000 concurrent requests: Virtual Threads consume approximately 420 MB of heap while maintaining about 4,800 virtual threads plus 8 carrier threads. p99 latency is 95ms. Spring WebFlux uses approximately 380 MB of heap with only 8 Netty event loop threads. p99 latency is 88ms. A 200-platform-thread configuration uses approximately 620 MB of heap while queuing 4,800 requests — p99 latency spikes to 1,240ms. A 1,000-platform-thread configuration uses approximately 1.4 GB of heap — mostly just thread stack memory — and achieves 380ms p99 latency.

What these numbers mean: Virtual Threads match WebFlux throughput while using standard imperative code. Platform threads at the typical default pool size are severely limited at high concurrency. To approach Virtual Thread throughput using platform threads requires approximately 1,000 threads — consuming around 1 GB just for thread stacks — and context-switching overhead still creates a ceiling. Virtual Threads deliver WebFlux-level performance without the reactive programming learning curve or codebase complexity.

7. Database Integration: JPA, JDBC, and Connection Pools

Database access is the most common I/O operation in REST APIs, and it is where Virtual Threads create an important configuration consideration that developers often miss.

With platform threads, your thread pool size naturally caps concurrent database calls. A 200-thread pool means at most 200 simultaneous database queries. With Virtual Threads, you can have thousands of concurrent requests all trying to acquire a database connection simultaneously. If your HikariCP pool has only 10 connections, 9,990 virtual threads will queue waiting for a connection. The connection pool becomes the bottleneck, not the thread count.

The connection pool sizing rule for Virtual Threads is: size your pool based on what your database can handle, not based on thread counts. A dedicated PostgreSQL instance typically handles 100 to 200 concurrent connections well. MySQL and MariaDB have similar characteristics. Set your HikariCP maximum-pool-size to a value your database supports — typically 50 to 150 connections for a microservice database. Set a short connection-timeout (2,000 to 3,000 milliseconds) so requests fail fast rather than queuing indefinitely when the pool is saturated.

Recommended HikariCP configuration for Virtual Thread applications: maximum-pool-size of 50, minimum-idle of 10, connection-timeout of 3000 milliseconds, idle-timeout of 300,000 milliseconds, max-lifetime of 1,200,000 milliseconds, and leak-detection-threshold of 5,000 milliseconds for development environments.

On the question of reactive database drivers: should you use R2DBC with Virtual Threads? In 2026, the recommendation is to use standard JDBC and JPA with Virtual Threads. R2DBC adds complexity and is designed specifically for WebFlux. Virtual Threads make blocking JDBC just as scalable for most REST API workloads. Keep your stack simple: standard JPA, HikariCP, and Virtual Threads is the right choice for new Spring Boot 3 projects targeting high concurrency.

Also set spring.jpa.open-in-view=false. Open Session in View holds database connections for the full HTTP request duration. With Virtual Threads and high concurrency, this anti-pattern becomes significantly more damaging than it is with platform thread pools.

8. Pitfalls and Anti-Patterns to Avoid

Pitfall 1: Synchronized Blocks Cause Thread Pinning

This is the most critical pitfall. When a Virtual Thread executes code inside a synchronized block or synchronized method and then blocks on I/O, the JVM cannot unmount it from its carrier thread. The carrier thread is pinned — it behaves exactly like a blocked platform thread. If many virtual threads pin their carriers simultaneously, you can exhaust the carrier thread pool entirely and create a deadlock-like situation.

The fix is straightforward: replace synchronized blocks and methods with ReentrantLock. ReentrantLock is virtual-thread-aware — the JVM can unmount a virtual thread that is waiting on a ReentrantLock, freeing the carrier for other work.

Detect pinning during development and load testing by adding the JVM flag -Djdk.tracePinnedThreads=full. This prints a stack trace whenever a virtual thread is pinned, identifying exactly which code and which library is causing the pinning.

The pinning risk is not limited to your own code. Third-party libraries that internally use synchronized blocks can also pin virtual threads. Common culprits include older JDBC drivers, certain security libraries, and legacy serialization frameworks. Use the trace flag during load testing on your full application to identify pinning across your entire dependency tree.

Pitfall 2: Heavy ThreadLocal Usage at Scale

Virtual Threads are cheap to create, but ThreadLocal variables still carry a per-thread memory cost. If you store large objects in ThreadLocal and create millions of virtual threads, memory consumption grows significantly. Java 21 introduces Scoped Values as the preferred alternative for passing contextual data in virtual thread applications. Scoped Values are automatically cleaned up when their scope exits, carry no per-thread memory accumulation, and are more semantically appropriate for the request-scoped data that ThreadLocal is typically used for in web applications.

Pitfall 3: Using Virtual Threads for CPU-Intensive Work

Do not submit long-running, CPU-intensive tasks to Virtual Threads and expect a performance gain. Image processing, machine learning inference, cryptographic operations, complex mathematical computation — all of these should use a dedicated ForkJoinPool or a separate platform thread executor. CPU-bound work keeps the carrier thread occupied for its entire duration, preventing other virtual threads from running on that carrier.

Pitfall 4: Ignoring Global Virtual Thread Enablement Scope

When spring.threads.virtual.enabled=true is set, Spring Boot applies Virtual Threads globally — to Tomcat, to @Async methods, and to @Scheduled tasks. If you have a specific scheduled task or async operation that performs CPU-intensive work, override that executor explicitly to use a platform thread pool rather than allowing it to run on virtual threads.

9. Production Configuration Checklist

Here is the complete recommended application.properties for a production Spring Boot Virtual Threads deployment in 2026:

Enable virtual threads: spring.threads.virtual.enabled=true

Tomcat configuration: server.tomcat.max-connections=25000, server.tomcat.accept-count=2000, server.tomcat.connection-timeout=10000, server.tomcat.threads.max=400.

HikariCP connection pool: spring.datasource.hikari.maximum-pool-size=50, spring.datasource.hikari.minimum-idle=15, spring.datasource.hikari.connection-timeout=3000, spring.datasource.hikari.idle-timeout=300000, spring.datasource.hikari.max-lifetime=1200000, spring.datasource.hikari.keepalive-time=60000, spring.datasource.hikari.leak-detection-threshold=5000.

JPA configuration: spring.jpa.open-in-view=false, spring.jpa.properties.hibernate.default_batch_fetch_size=25, spring.jpa.properties.hibernate.jdbc.batch_size=25, spring.jpa.properties.hibernate.order_inserts=true, spring.jpa.properties.hibernate.order_updates=true.

Actuator and observability: management.endpoints.web.exposure.include=health,metrics,threaddump,prometheus, management.endpoint.health.show-details=when-authorized, management.metrics.distribution.percentiles-histogram.http.server.requests=true.

Recommended JVM flags for production: -XX:+UseG1GC, -XX:MaxGCPauseMillis=200, -Xms512m, -Xmx2g. Remove -Djdk.tracePinnedThreads=full from production after validation in load testing.

Pre-deployment validation checklist: confirm java -version shows Java 21 or later, confirm the thread-info endpoint returns isVirtual=true, run a load test at target concurrency and verify pinning trace shows no pinned threads, confirm HikariCP pool size aligns with database connection limits, confirm spring.jpa.open-in-view is false, confirm all synchronized blocks in your code have been replaced with ReentrantLock, and confirm third-party library pinning has been evaluated under load.

10. Frequently Asked Questions

What are Virtual Threads in Java 21?

Virtual Threads are lightweight threads managed entirely by the JVM rather than the operating system, introduced as a stable production-ready feature in Java 21 LTS through Project Loom. Unlike traditional platform threads which each consume approximately 1 MB of memory and a dedicated OS thread, Virtual Threads start at just a few kilobytes and the JVM can create millions of them. The JVM multiplexes them onto a small pool of carrier threads, automatically unmounting a Virtual Thread when it blocks on I/O and remounting it when I/O completes — all transparently, without any code changes required.

How do I enable Virtual Threads in Spring Boot 3?

Add spring.threads.virtual.enabled=true to your application.properties file. Spring Boot 3.2 and later automatically configures Tomcat, the async task executor, and the scheduled task executor to use Virtual Threads when this property is set. You must be running Java 21 or later.

Is Spring Boot with Virtual Threads better than WebFlux?

For most I/O-bound REST API workloads, Spring Boot with Virtual Threads delivers throughput that matches or is comparable to WebFlux while using familiar imperative programming style. WebFlux remains advantageous for streaming responses, fine-grained backpressure control, and teams already deeply invested in reactive programming patterns. For new Spring Boot 3 projects starting in 2026, Spring MVC plus Virtual Threads is the recommended starting point — simpler to write, test, and debug, with performance that matches reactive stacks for typical workloads.

What Java version is required for Virtual Threads in Spring Boot?

Java 21 or higher is required. Virtual Threads were available as preview features in Java 19 and 20 but became a stable production-ready feature in Java 21 LTS. Spring Boot 3.2 and later provide first-class support through the spring.threads.virtual.enabled configuration property.

Do Virtual Threads work with Spring Data JPA?

Yes, Spring Data JPA works fully with Virtual Threads. The key consideration is your HikariCP connection pool configuration. With Virtual Threads, you can have many more concurrent database calls than a traditional thread pool would allow, so size your pool based on what your database can handle — typically 50 to 150 connections — rather than based on thread count. Always set spring.jpa.open-in-view=false to avoid holding database connections for the full HTTP request lifecycle.

What is thread pinning and how do I avoid it?

Thread pinning occurs when a Virtual Thread executing inside a synchronized block blocks on I/O. The JVM cannot unmount it from the carrier thread, which remains occupied for the entire duration of the I/O wait — negating the Virtual Thread benefit. The fix is to replace synchronized blocks and methods with ReentrantLock, which is virtual-thread-aware. Detect pinning during load testing using the JVM flag -Djdk.tracePinnedThreads=full.

How do Virtual Threads affect HikariCP connection pool configuration?

With Virtual Threads, thousands of concurrent requests may simultaneously attempt to acquire database connections. If your pool is too small, virtual threads will queue waiting for connections. Size your HikariCP maximum-pool-size based on what your database supports — typically 50 to 150 for a microservice workload. Set a short connection-timeout of 2,000 to 3,000 milliseconds so requests fail fast rather than creating an invisible queue.

How to use Virtual Threads in Spring Boot 3 for high-performance REST API development?

The complete path is: use Java 21 and Spring Boot 3.2 or later, add spring.threads.virtual.enabled=true to application.properties, write standard blocking Spring MVC controllers and services with no reactive code required, configure HikariCP pool size based on database capacity rather than thread count, replace any synchronized blocks with ReentrantLock, and run load tests with -Djdk.tracePinnedThreads=full to detect and eliminate pinning. That is the complete process for high-performance REST API development with Spring Boot Virtual Threads in 2026.

Conclusion

Spring Boot Virtual Threads represent the most significant shift in Java backend development since the introduction of Spring Boot itself. The combination of Java 21 LTS stability, Spring Boot 3.2's seamless one-property enablement, and Virtual Threads' ability to deliver reactive performance with imperative code makes this the clear architectural choice for new high-throughput REST API development in 2026.

The key points to carry forward: Virtual Threads require Java 21 and Spring Boot 3.2 or later. Enablement is a single property. Your existing blocking code works without changes. Replace synchronized with ReentrantLock to prevent pinning. Size your HikariCP pool by database capacity, not thread count. Disable open-in-view. Virtual Threads match WebFlux throughput for I/O-bound workloads while keeping code simple and readable.

The worst decision is spending months evaluating rather than building. Enable Virtual Threads in your next Spring Boot project. Write simple blocking code. Run the benchmarks yourself. The results will speak for themselves.

Ready to Master Spring Boot Virtual Threads and Java 21?

JustAcademy's Full Stack Java Developer Bootcamp covers Spring Boot 3.x, Virtual Threads, REST API development, microservices architecture, database integration, and everything you need to land a high-paying Java developer role in 2026. Live instructor-led sessions, real-world project portfolio, recognized certification, and 100% placement support through 650+ hiring partners.

Enroll here: https://www.justacademy.co/job-bootcamp-detail/full-stack-java-developer-bootcamp-online-live-interactive-training-with-real-projects

Book a free demo session: https://www.justacademy.co/register-for-course-demo

Download the course brochure: https://www.justacademy.co/download-general-brochure

Related Resources

Advanced Java Certification Training in Mumbai: https://www.justacademy.co/blog-detail/advanced-java-certification-training-course-in-mumbai-elevate-your-java-expertise-with-advanced-java-training

Full Stack Java Developer Bootcamp: https://www.justacademy.co/job-bootcamp-detail/full-stack-java-developer-bootcamp-online-live-interactive-training-with-real-projects

Data Analytics Bootcamp: https://www.justacademy.co/job-bootcamp-detail/data-analytics-bootcamp-online-live-training-with-real-world-projects

MEAN Stack Developer Bootcamp: https://www.justacademy.co/job-bootcamp-detail/mean-stack-developer-bootcamp-full-stack-web-development-training-program

JustAcademy | 1201, 12th Floor, Star Plaza, Borivali East, Mumbai 400066 | +91 99871 84296 | www.justacademy.co

What Are Virtual Threads in Java 21 and How Do They Work with Spring Boot?

How to Enable and Configure Virtual Threads in Spring Boot 3.x Step by Step

Virtual Threads vs Platform Threads vs WebFlux: Performance Benchmarks and Real-World Results in 2026

Common Pitfalls, Anti-Patterns, and Production Best Practices for Spring Boot Virtual Threads

Connect With Us
whatsapp